package com.example.easycharge.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.easycharge.annotation.LogAop;
import com.example.easycharge.annotation.PassToken;
import com.example.easycharge.entity.Customer;
import com.example.easycharge.error.NoTokenError;
import com.example.easycharge.repository.CustomerRepository;
import com.example.easycharge.repository.MessageItemRepository;
import com.example.easycharge.repository.VehicleRepository;
import com.example.easycharge.wrapper.SelfRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 拦截器，负责接收login参数并根据内容返回token
 */
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
    //使用userRepository来获取用户
    @Autowired
    private CustomerRepository customerRepository;
    @Autowired
    private VehicleRepository vehicleRepository;
    @Autowired
    private MessageItemRepository messageItemRepository;

    @LogAop(value = "进入身份验证")
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws IOException, NoTokenError {
        String token = httpServletRequest.getHeader("token");
        String requestPara = null;
        JSONObject requestJson = null;
        String tempUrl = httpServletRequest.getRequestURI();
        try {
            if (tempUrl.equals("/error")) {
                return true;
            }
            requestPara = ((SelfRequestWrapper) httpServletRequest).getBodyStr();
            requestJson = JSONObject.parseObject(requestPara);
        } catch (Exception e) {
            errorHandler("请求URL错误，拒绝访问", httpServletResponse);
        }
//        System.out.println(requestPara);
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }

        // 对所有接口都验证,除非被PassToken注解修饰
//        if (method.isAnnotationPresent(UserLoginToken.class)) {
//        UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
//        if (userLoginToken.required()) {
        // 执行认证
        if (token == null || token.length() == 0) {
            errorHandler("请求头中未包含Token字段", httpServletResponse);
            return false;
        }
        // 这里检查token有没有过期
        DecodedJWT decodedJWT = JWT.decode(token);
        try {
            long expireTime = decodedJWT.getClaim("expireTime").asLong();
            long nowTime = new Date().getTime();
            if (nowTime - expireTime > 72L * 24 * 60 * 60 * 1000) {
                errorHandler("token过期，请重新登录", httpServletResponse);
                return false;
            }
        } catch (JWTDecodeException e) {
            errorHandler("token缺乏关键信息，拒绝访问", httpServletResponse);
            return false;
        }

        // 这里检查是否包含必要的信息，同时获取数据库中的用户
        String userName;
        long userId;
        long roleId;
        try {
            userName = decodedJWT.getClaim("userName").asString();
            userId = decodedJWT.getClaim("userId").asLong();
            roleId = decodedJWT.getClaim("roleId").asLong();
        } catch (JWTDecodeException j) {
            errorHandler("token缺乏关键信息，拒绝访问", httpServletResponse);
            return false;
        }
        Customer customer = customerRepository.findCustomerByUserName(userName);
        if (customer == null) {
            errorHandler("用户不存在，请重新登录", httpServletResponse);
            return false;
        }

        // 这里是验证token是否正确加密，验证内容是否被更改
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(customer.getUserPassword())).build();
        try {
            jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
            errorHandler("JWT加密错误，拒绝访问", httpServletResponse);
            return false;
        }
        try {
            if (!resourceCheck(httpServletRequest.getRequestURI(), token, requestJson)) {
                errorHandler("不具有此操作权限，拒绝访问", httpServletResponse);
                return false;
            }
            httpServletRequest.setAttribute("customerId", userId);
            return true;
        } catch (Exception e) {
            errorHandler("JWT身份验证失败，拒绝访问", httpServletResponse);
            return false;
        }

//        }
    }

    // 验证是否具有该资源的操作权限
    // TODO: 2022-05-09 新增接口的权限验证
    @LogAop(value = "权限验证")
    private boolean resourceCheck(String requestURL, String token, JSONObject requestBody) throws Exception {
        DecodedJWT decodedJWT = JWT.decode(token);
        requestURL = requestURL.substring(1);
        long userId = decodedJWT.getClaim("userId").asLong();
        long roleId = decodedJWT.getClaim("roleId").asLong();
        // 角色为3表示超级管理员，直接跳过资源身份验证
        if (roleId == 3) {
            return true;
        }
        if (requestURL.equals("SSE"))
            return true;
        // 在这里对URL做前缀解析，然后做权限验证
        if (requestURL.startsWith("report")) {
            return roleId == 2;
        }
        if (requestURL.startsWith("alert")) {
            return roleId == 2;
        }
        if (requestURL.startsWith("conf")) {
            return roleId == 2;
        }
        if (requestURL.startsWith("bill/admin")) {
            // 判断是否是管理员，只有管理员才可以有访问权限
            return roleId == 2;
        }

        if (requestURL.startsWith("charge")) {
            String subUrl = requestURL.substring(6);

            if (subUrl.length() == 0) {
                long vehicleId = requestBody.getLong("vehicleId");
                // 判断这个请求里的车是不是自己的车
                int num = vehicleRepository.countVehicleByVehicleIdAndCustomerId(vehicleId, userId);
                return num == 1;
            }
            subUrl = subUrl.substring(1);
            if (subUrl.startsWith("shortest")) {
                return true;
            }

            String tempUrl = subUrl.substring(0, subUrl.indexOf('/'));
            if (tempUrl.startsWith("length") || tempUrl.startsWith("waitTime") || tempUrl.startsWith("state")) {
                return true;
            }
            /*
                cancel/
                plugIn/
                unplug/
                driveIn/
                driveAway/
                pattern/
                capacity/
             */
            subUrl = subUrl.substring(subUrl.indexOf('/') + 1);
            long vehicleId;
            if (tempUrl.equals("pattern")) {
                vehicleId = Long.parseLong(subUrl.substring(0, subUrl.indexOf('/')));
            } else {
                vehicleId = Long.parseLong(subUrl);
            }
            int num = vehicleRepository.countVehicleByVehicleIdAndCustomerId(vehicleId, userId);
            return num == 1;
        }
        if (requestURL.startsWith("user/")) {
            String subUrl = requestURL.substring(5);
            if (subUrl.startsWith("totalVehicle") || subUrl.startsWith("setting") || subUrl.startsWith("bill")) {
                return true;
            }
            if (subUrl.startsWith("admin/")) {
                return roleId == 2;
            }
            if (subUrl.startsWith("vehicle/")) {
                subUrl = subUrl.substring(8);
                long vehicleId = Long.parseLong(subUrl);
                int num = vehicleRepository.countVehicleByVehicleIdAndCustomerId(vehicleId, userId);
                return num == 1;
            }
            return false;
        }
        if (requestURL.startsWith("messageItem/")) {
            String subUrl = requestURL.substring("messageItem/".length());
            if (subUrl.startsWith("admin")) {
                return roleId == 2;
            }
            // 懒得改了，直接过吧，毁灭吧
//            if (subUrl.startsWith("readMsg/")) {
//                long messageItem = Long.parseLong(subUrl.substring(8));
//                int num = messageItemRepository.countMessageItemByMessageItemIdAndCustomerId(messageItem, userId);
//                return num == 1;
//            }
//            if (subUrl.startsWith("readAll")) {
//                return true;
//            }
//            int index = subUrl.indexOf('/');
//            if (index == -1) {
//                long msgId = Long.parseLong(subUrl);
//                return messageItemRepository.countMessageItemByMessageItemIdAndCustomerId(msgId, userId) == 1;
//            }
//            int page = Integer.parseInt(subUrl.substring(0, index));
//            int num = Integer.parseInt(subUrl.substring(index + 1));
            return true;
        }
        if (requestURL.startsWith("pile")) {
            return roleId == 2;
        }
        if (requestURL.startsWith("queue/")) {
            String subUrl = requestURL.substring(6);
            if (subUrl.startsWith("admin")) {
                return roleId == 2;
            }
            return true;
        }
        if (requestURL.startsWith("record/")) {
            String subUrl = requestURL.substring(7);
            if (subUrl.startsWith("admin")) {
                return roleId == 2;
            }
            return true;
        }
        if (requestURL.startsWith("vehicle")) {
            String subUrl = requestURL.substring(7);
            if (subUrl.length() == 0) {
                return true;
            }
            subUrl = subUrl.substring(1);
            if (roleId == 2)
                return true;
            long vehicleId;
            if (subUrl.startsWith("setting")) {
                vehicleId = Long.parseLong(subUrl.substring(8, subUrl.indexOf('/', 8)));
            } else {
                vehicleId = Long.parseLong(subUrl);
            }
            int num = vehicleRepository.countVehicleByVehicleIdAndCustomerId(vehicleId, userId);
            return num == 1;
        }
        return false;
    }

    private void errorHandler(String errorMessage, HttpServletResponse httpServletResponse) throws IOException {
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        //权限校验不通过，禁止访问
        httpServletResponse.setStatus(403);
        PrintWriter out = null;
        JSONObject res = new JSONObject();
        res.put("success", "false");
        res.put("msg", errorMessage);
        out = httpServletResponse.getWriter();
        out.append(res.toString());
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {

    }
}
